package com.lijianlove.log.loghelper.utils;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.filter.Filter;
import lombok.AllArgsConstructor;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentMap;

/**
 * @author lijian
 * @date 2019/5/7 下午2:58
 */
@Slf4j
@UtilityClass
public class ChangeLogLevelProcessUnit {

    private static String logFrameworkType = "LOGBACK";
    private static Map<String, Object> loggerMap = new HashMap<>();
    private static Map<Integer, Level> levelMap = new HashMap<>();
    /**
     * 调用修改会将修改之前的日志级别放到 resetmap
     */
    private static Map<String, Level> resetMap = new HashMap<>();
    /**
     * 记录系统设置的日志边界
     */
    private static String initThresholdFilterLevel = null;

    static {
        // 初始化 level map
        initLevelMap();
        // 初始化 logger map
        initLoggerMap();
    }

    private static void initLevelMap() {
        for (LevelItem item : LevelItem.values()) {
            levelMap.put(item.level, item.realLevel);
        }
    }

    @AllArgsConstructor
    enum LevelItem {
        DEBUG(1, Level.DEBUG),
        INFO(2, Level.INFO),
        WARN(3, Level.WARN),
        ERROR(4, Level.ERROR);

        private Integer level;
        private Level realLevel;
    }

    private static void initLoggerMap() {
        String type = StaticLoggerBinder.getSingleton().getLoggerFactoryClassStr();
        if ("ch.qos.logback.classic.util.ContextSelectorStaticBinder".equals(type)) {
            logFrameworkType = "LOGBACK";
            ch.qos.logback.classic.LoggerContext loggerContext = (ch.qos.logback.classic.LoggerContext) LoggerFactory.getILoggerFactory();
            for (Logger logger : loggerContext.getLoggerList()) {
//                if (logger.getLevel() != null) {
                initThresholdFilterLevel(logger);
                loggerMap.put(logger.getName(), logger);
//                }
            }
            Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
            loggerMap.put(rootLogger.getName(), rootLogger);
        }
    }

    public static Map<String, Object> getLoggerList() {
        Map<String, Object> result = new HashMap<>(16);
        result.put("logFramework", logFrameworkType);
        List<Map<String, Object>> loggerList = new ArrayList<>();
        for (ConcurrentMap.Entry<String, Object> entry : loggerMap.entrySet()) {
            Map<String, Object> loggerJSON = new HashMap<>(64);
            loggerJSON.put("loggerName", entry.getKey());
            if ("LOGBACK".equalsIgnoreCase(logFrameworkType)) {
                Logger targetLogger = (Logger) entry.getValue();
                loggerJSON.put("logLevel", targetLogger.getLevel() != null ? targetLogger.getLevel().toString() : null);
            }
            loggerList.add(loggerJSON);
        }
        result.put("loggerList", loggerList);

        return result;
    }

    public static Map<String, Object> setLevel(String beanName, Integer level) {
        log.info("setLevel beanName {} , level {}", beanName, level);
        PathMatchingResourcePatternResolver resourceLoader = new PathMatchingResourcePatternResolver();

        Level setLevel = levelMap.get(level);
        resetMap.clear();
        if (setLevel != null) {
            for (String name : loggerMap.keySet()) {
                if (Objects.nonNull(beanName) && beanName.trim().length() > 0) {
                    boolean match = resourceLoader.getPathMatcher().match(beanName, name);
                    ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) loggerMap.get(name);
                    resetThresholdFilterLevel(logger, setLevel);
                    if (match) {
                        resetMap.put(name, logger.getLevel());
                        logger.setLevel(setLevel);
                    }

                }
            }
        }
        initLoggerMap();
        return getLoggerList();
    }

    public static Map<String, Object> reset() {
        for (String name : loggerMap.keySet()) {

            ch.qos.logback.classic.Logger logger = (ch.qos.logback.classic.Logger) loggerMap.get(name);
            resetThresholdFilterLevel(logger, Level.valueOf(initThresholdFilterLevel));

            if (resetMap.containsKey(name)) {
                Level oldLevel = resetMap.get(name);
                logger.setLevel(oldLevel);
            }
        }
        initLoggerMap();
        return getLoggerList();
    }


    /**
     * 记录初始的 console level 边界值
     *
     * @param logger
     */
    private static void initThresholdFilterLevel(Logger logger) {
        if (logger != null) {
            Appender<ILoggingEvent> console = logger.getAppender("console");
            if (console != null) {
                List<Filter<ILoggingEvent>> copyOfAttachedFiltersList = console.getCopyOfAttachedFiltersList();
                if (copyOfAttachedFiltersList != null && copyOfAttachedFiltersList.size() > 0) {
                    for (Filter<ILoggingEvent> loggingEventFilter : copyOfAttachedFiltersList) {
                        if (loggingEventFilter != null) {
                            if (loggingEventFilter instanceof ThresholdFilter) {
                                if (initThresholdFilterLevel == null) {
                                    ThresholdFilter thresholdFilter = (ThresholdFilter) (logger.getAppender("console").getCopyOfAttachedFiltersList().get(0));
                                    log.info("--");
                                    Field field = null;
                                    try {
                                        field = thresholdFilter.getClass().getDeclaredField("level");
                                    } catch (NoSuchFieldException e) {
                                        e.printStackTrace();
                                    }
                                    try {
                                        field.setAccessible(true);
                                        Level level = (Level) field.get(thresholdFilter);
                                        initThresholdFilterLevel = level.levelStr;
                                        field.setAccessible(false);
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    }
                                }

                            }
                        }
                    }
                }
            }
        }
    }

    // Level.toLevel(initThresholdFilterLevel)
    private static void resetThresholdFilterLevel(Logger logger, Level level) {
        if (logger != null) {
            Appender<ILoggingEvent> console = logger.getAppender("console");
            if (console != null) {
                List<Filter<ILoggingEvent>> copyOfAttachedFiltersList = console.getCopyOfAttachedFiltersList();
                if (copyOfAttachedFiltersList != null && copyOfAttachedFiltersList.size() > 0) {
                    for (Filter<ILoggingEvent> loggingEventFilter : copyOfAttachedFiltersList) {
                        if (loggingEventFilter != null) {
                            if (loggingEventFilter instanceof ThresholdFilter) {
                                if (initThresholdFilterLevel != null) {
                                    ThresholdFilter thresholdFilter = (ThresholdFilter) (logger.getAppender("console").getCopyOfAttachedFiltersList().get(0));
                                    Field field = null;
                                    try {
                                        field = thresholdFilter.getClass().getDeclaredField("level");
                                    } catch (NoSuchFieldException e) {
                                        e.printStackTrace();
                                    }
                                    try {
                                        field.setAccessible(true);
                                        field.set(thresholdFilter, level);
                                        field.setAccessible(false);
                                    } catch (IllegalAccessException e) {
                                        e.printStackTrace();
                                    }
                                }

                            }
                        }
                    }
                }
            }
        }
    }

}
